// Copyright (c) 2019 Presto Labs Pte. Ltd.
// Author: jaewon

#pragma once

#include <cstdint>
#include <optional>

#include "coin1/fastfeed/price_map.h"
#include "coin1/fastfeed/view/base.h"
#include "coin1/fastfeed/view/trade_builder.h"

namespace fastfeed {
namespace view {

struct BookLevel {
  int64_t price = 0;
  int64_t qty = 0;
  int64_t count = 0;
};  // struct BookLevel

class BookIterator {
 public:
  BookIterator() = default;
  BookIterator(const BookIterator& it) = default;
  BookIterator(BookIterator&& it) = default;
  explicit BookIterator(Int64PriceMapConstIterator<BookLevel> underlying_it) : it_(underlying_it) {}
  ~BookIterator() = default;

  bool operator==(const BookIterator& rhs) { return it_ == rhs.it_; }

  bool operator!=(const BookIterator& rhs) { return it_ != rhs.it_; }

  BookIterator& operator++() {  // Pre-increment
    ++it_;
    return *this;
  }

  BookIterator operator++(int) {  // Post-increment
    BookIterator ret(*this);
    ++it_;
    return ret;
  }

  const BookLevel& operator*() const { return it_->second; }

  const BookLevel* operator->() const { return &it_->second; }

 private:
  Int64PriceMapConstIterator<BookLevel> it_;
};  // class BookIterator

class BookBuilder : public detail::BookBuilderParameterHolder {
 public:
  ~BookBuilder() final = default;

  void Init(ViewCollection* view_collection) final;
  bool UpdateByProto(const proto::Feed& feed) final;

  // View methods
  // Ask
  bool IsAskEmpty() const { return ask_.empty(); }
  int32_t GetAskDepth() const { return ask_.depth(); }
  std::optional<int64_t> GetAsk0Price() const { return ask_.min_price(); }
  std::optional<BookLevel> GetAsk0() const;
  std::optional<BookLevel> GetAskLevelByPrice(
      int64_t price,
      bool return_next_level_if_not_exist = false) const;

  BookIterator AskBegin() const { return BookIterator(ask_.begin()); }
  BookIterator AskEnd() const { return BookIterator(ask_.end()); }
  BookIterator FindAskLevelByPrice(int64_t price, bool return_next_level_if_not_exist = false)
      const;

  // Bid
  bool IsBidEmpty() const { return bid_.empty(); }
  int32_t GetBidDepth() const { return bid_.depth(); }
  std::optional<int64_t> GetBid0Price() const { return bid_.max_price(); }
  std::optional<BookLevel> GetBid0() const;
  std::optional<BookLevel> GetBidLevelByPrice(
      int64_t price,
      bool return_next_level_if_not_exist = false) const;

  BookIterator BidBegin() const { return BookIterator(bid_.begin()); }
  BookIterator BidEnd() const { return BookIterator(bid_.end()); }
  BookIterator FindBidLevelByPrice(int64_t price, bool return_next_level_if_not_exist = false)
      const;

 private:
  bool OnBookSnapshot(const proto::Feed& feed);
  bool OnBookUpdate(const proto::Feed& feed);
  void CleanupBook(
      const std::optional<int64_t>& min_ask_price,
      const std::optional<int64_t>& max_bid_price);

  AskInt64PriceMap<BookLevel> ask_;
  BidInt64PriceMap<BookLevel> bid_;
};  // class BookBuilder

}  // namespace view
}  // namespace fastfeed
